package com.gjs.management.filter;

import cn.hutool.core.date.DateUtil;
import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import cn.hutool.jwt.JWTUtil;
import com.alibaba.fastjson.JSON;
import com.gjs.common.manager.response.RspBody;
import com.gjs.common.manager.sso.AccessTokenRsp;
import com.gjs.common.manager.sso.SsoClient;
import com.gjs.common.pojo.dto.user.UserInfoDTO;
import lombok.Getter;
import lombok.Setter;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisOperations;
import org.springframework.security.authentication.AuthenticationCredentialsNotFoundException;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;

import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.BufferedReader;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.TimeUnit;

@Slf4j
public class JWTAuthenticationFilter extends UsernamePasswordAuthenticationFilter {

    @Setter
    private String secretKey;

    @Setter
    private String cacheKey;

    @Setter
    private Long cacheExpire;

    @Autowired
    private RedisOperations<String, Object> redisUtil;

    @Autowired
    private SsoClient ssoClient;

    public JWTAuthenticationFilter(AuthenticationManager authenticationManager) {
        super.setAuthenticationManager(authenticationManager);
    }

    /**
     * 登录处理方法
     */
    @Override
    public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException {
        UserInfoDTO userInfoDTO;
        AccessTokenRsp accessTokenRsp;
        //自定义从body中获取json格式数据
        StringBuffer sb = new StringBuffer();
        String line = null;
        try {
            BufferedReader reader = request.getReader();
            while ((line = reader.readLine()) != null) {
                sb.append(line);
            }
        } catch (Exception e) {
            throw new AuthenticationCredentialsNotFoundException("参数错误");
        }
        //将空格和换行符替换掉避免使用反序列化工具解析对象时失败
        String jsonString = sb.toString().replaceAll("\\s", "").replaceAll("\n", "");
        JSONObject jsonObject = JSONUtil.parseObj(jsonString);
        try {
            String code = jsonObject.getStr("code");
            if (code == null) {
                throw new IllegalArgumentException("缺少授权码");
            }

            accessTokenRsp = ssoClient.getAccessToken(code, "all");
            userInfoDTO = ssoClient.getUserInfo(accessTokenRsp.getAccessToken());

        } catch (IOException e) {
            throw new UsernameNotFoundException(e.getMessage());
        }
        // ssoClient.getUserInfo成功代表SSO用户校验完成，注入一个UsernamePasswordAuthenticationToken继续校验是否这个平台的工作人员
        UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(userInfoDTO.getUserSn(), userInfoDTO.getUserSn());
        authenticationToken.setDetails(userInfoDTO); // 设置details为userInfoRsp
        return super.getAuthenticationManager().authenticate(authenticationToken);
    }

    /**
     * 登录成功后调用的方法
     */
    @Override
    protected void successfulAuthentication(HttpServletRequest request, HttpServletResponse response, FilterChain chain, Authentication authResult) throws IOException {
        // 组装token载荷
        Map<String, Object> map = new HashMap<>();
        map.put("userSn", authResult.getName());
        map.put("issuedAt", DateUtil.date());
        // 生成token
        String token = JWTUtil.createToken(map, secretKey.getBytes());
        // 缓存token到redis，保存登录态
        redisUtil.opsForValue().set(cacheKey + authResult.getName(), JSON.toJSONString(authResult), cacheExpire, TimeUnit.SECONDS);
        // 输出响应
        response.setContentType("application/json;charset=UTF-8");
        response.getWriter().write(JSONUtil.toJsonStr(RspBody.success(token)));
    }

    /**
     * 登录失败后调用的方法
     */
    @Override
    protected void unsuccessfulAuthentication(HttpServletRequest request, HttpServletResponse response, AuthenticationException failed) throws IOException, ServletException {
        response.setContentType("application/json;charset=UTF-8");
        response.getWriter().write(JSONUtil.toJsonStr(RspBody.forbid(failed.getMessage())));
    }
}
